package com.leinao.media.utils;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.leinao.media.entity.*;
import com.leinao.media.enums.HkErrorEnum;
import com.leinao.media.exception.MediaException;
import com.leinao.media.sdk.hk.HCNetSDK;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * 海康设备操作工具类
 *
 * @author zhangyu
 * @date 2022/3/15 15:19
 */
public class HkUtil {

    private static final Log log = LogFactory.get();

    /**
     * 实例化SDK
     */
    private static final HCNetSDK HC_NET_SDK = HCNetSDK.INSTANCE;

    /**
     * 设备信息map
     */
    static Map<String, HCNetSDK.NET_DVR_DEVICEINFO_V40> DVR_INFO_MAP = new HashMap<>();

    /**
     * 文件查找句柄
     */
    private static Map<String, Integer> handleCache = new HashMap<>();

    /**
     * sdk 初始化
     * 对整个网络 SDK 系统的初始化，内存预分配等操作
     */
    public static void init() {
        if (!HC_NET_SDK.NET_DVR_Init()) {
            log.error("HK_SDK初始化失败");
        }
        log.info("海康SDK完成初始化...");
        // 开启日志
        File path = new File(".");
        boolean bRet = HC_NET_SDK.NET_DVR_SetLogToFile(3, path.getAbsoluteFile().getParent() +
                File.separator + "sdklog" + File.separator + "HK" + File.separator, false);
        if (!bRet) {
            log.error("海康SDK日志开启失败,错误码:{},错误原因：{}",
                    HC_NET_SDK.NET_DVR_GetLastError(), HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
    }

    /**
     * 激活设备
     *
     * @param ip       IP
     * @param port     端口号
     * @param password 初始密码
     */
    public static String activateDevice(String ip, String port, String password) {
        // 设备激活参数结构体
        HCNetSDK.NET_DVR_ACTIVATECFG activateCfg = new HCNetSDK.NET_DVR_ACTIVATECFG();
        activateCfg.dwSize = activateCfg.size();
        // 初始密码
        activateCfg.sPassword = password.getBytes();
        activateCfg.write();
        // 设备激活
        boolean deviceBool = HC_NET_SDK.NET_DVR_ActivateDevice(ip, Short.parseShort(port), activateCfg);
        if (deviceBool) {
            log.info("设备:{},激活成功!", ip);
            return "";
        }
        if (HC_NET_SDK.NET_DVR_GetLastError() == 252) {
            log.info("设备:{},已激活!", ip);
            return "设备：" + ip + "已激活,请勿重复操作!";
        }
        log.info("设备:{},激活失败!错误码：{},错误原因：{}", ip, HC_NET_SDK.NET_DVR_GetLastError(),
                HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        return "设备激活失败！";
    }

    /**
     * 设备注册
     *
     * @param ip       IP
     * @param port     端口
     * @param userName 用户名
     * @param password 密码
     * @return 用户ID
     */
    public static long login(String ip, String port, String userName, String password) {
        // NET_DVR_Login_V40()登录
        HCNetSDK.NET_DVR_USER_LOGIN_INFO pLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
        // IP
        System.arraycopy(ip.getBytes(), 0, pLoginInfo.sDeviceAddress, 0, ip.length());
        // port
        pLoginInfo.wPort = Short.parseShort(port);
        // username
        System.arraycopy(userName.getBytes(), 0, pLoginInfo.sUserName, 0, userName.length());
        // password
        System.arraycopy(password.getBytes(), 0, pLoginInfo.sPassword, 0, password.length());
        // 是否异步登录：0- 否，1- 是
        pLoginInfo.bUseAsynLogin = false;
        pLoginInfo.write();

        HCNetSDK.NET_DVR_DEVICEINFO_V40 DVR_INFO_V40 = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
        int uid = HC_NET_SDK.NET_DVR_Login_V40(pLoginInfo, DVR_INFO_V40);
        // 缓存设备信息
        DVR_INFO_MAP.put(ip, DVR_INFO_V40);
        if (uid == -1) {
            log.error("{},设备注册失败,错误码：{},错误原因：{}", ip, HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("海康设备注册失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        log.info(ip + "设备注册成功");
        return uid;
    }

    /**
     * 判断设备状态
     *
     * @param uid 用户ID
     * @return 在线/离线
     */
    public static String getDeviceStatus(long uid) {
        boolean isOnline = HC_NET_SDK.NET_DVR_RemoteControl((int) uid, HCNetSDK.NET_DVR_CHECK_USER_STATUS, null, 0);
        if (isOnline) {
            log.info("设备在线");
            return "在线";
        } else {
            log.error("判断设备状态出现错误,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            log.info("设备离线");
            return "离线";
        }
    }

    /**
     * 销毁当前登录信息
     *
     * @param uid 用户ID
     */
    public static void logout(long uid) {
        log.info("HK销毁当前登录信息,uid:{}", uid);
        boolean bool = HC_NET_SDK.NET_DVR_Logout((int) uid);
        log.info("HK销毁当前登录信息成功与否：{}", bool);
        if (!bool) {
            log.error("HK销毁当前登录信息错误，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("HK销毁当前登录信息错误,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
    }

    /**
     * 释放SDK资源
     */
    public static void cleanup() {
        boolean bool = HC_NET_SDK.NET_DVR_Cleanup();
        if (!bool) {
            log.error("释放SDK资源错误，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("释放SDK资源错误,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        log.info("海康SDK释放资源...");
//        System.exit(0);
    }

    /**
     * 获取设备硬盘信息
     *
     * @param uid 登录句柄
     * @return JSONObject
     */
    public static JSONObject getDeviceDiskInfo(long uid) {
        HCNetSDK.NET_DVR_WORKSTATE_V30 dvrWork = new HCNetSDK.NET_DVR_WORKSTATE_V30();
        if (!HC_NET_SDK.NET_DVR_GetDVRWorkState_V30((int) uid, dvrWork)) {
            // 返回Boolean值，判断是否获取设备能力
            log.error("返回设备状态失败,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            return new JSONObject();
        }
        // 总容量
        double sum = 0;
        // 剩余容量
        double free = 0;
        HCNetSDK.NET_DVR_DISKSTATE[] diskStatic = dvrWork.struHardDiskStatic;
        for (HCNetSDK.NET_DVR_DISKSTATE diskState : diskStatic) {
            if (diskState.dwVolume != 0) {
                sum = sum + NumberUtil.div(diskState.dwVolume, 1024, 2);
                free = free + NumberUtil.div(diskState.dwFreeSpace, 1024, 2);
            }
        }
        // 已用容量 = 总容量-剩余容量
        double used = NumberUtil.sub(sum, free);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("sum", sum);
        jsonObject.put("free", free);
        jsonObject.put("used", used);
        return jsonObject;
    }

    /**
     * 获取录像总时长
     *
     * @param uid 用户句柄
     * @return 录像总时长
     */
    public static long getSumVideoTime(String ip, long uid, String userName, String password) {
        // 获取录像总时长
        long sumTime = 0;
        // step：
        // 1.获取设备下面所有通道摄像机
        List<Camera> cameraList = getCameraList(ip, uid, userName, password);
        // 2.遍历每个摄像机，得到每个摄像机的录像起止时间
        for (Camera camera : cameraList) {
            String channel = camera.getChannel();
            JSONObject videoTime = searchVideoTime(uid, Integer.parseInt(channel));
            String startDate = NullUtil.valueOf(videoTime.get("startDate"));
            String endDate = NullUtil.valueOf(videoTime.get("endDate"));

            if (StrUtil.equals("0002-11-30 00:00:00", startDate)) {
                startDate = "2000-01-01 00:00:00";
            }
            if (StrUtil.equals("0002-11-30 00:00:00", endDate)) {
                endDate = DateUtil.now();
            }

            if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) {
                // 3.根据每个摄像机的录像起止时间，算出每个摄像机的录像总时长
                JSONObject videoFile = findVideoFile(uid, Integer.parseInt(channel), DateUtil.parse(startDate, "yyyy-MM-dd HH:mm:ss"),
                        DateUtil.parse(endDate, "yyyy-MM-dd HH:mm:ss"));
                Object sumVideoTime = videoFile.get("sumVideoTime");
                if (null != sumVideoTime) {
                    long time = (long) videoFile.get("sumVideoTime");
                    sumTime = sumTime + time;
                }
            }
        }
        return sumTime;
    }

    /**
     * 获取DVR下面的摄像机列表
     *
     * @param uid 用户ID
     * @return 摄像机列表
     */
    public static List<Camera> getCameraList(String ip, long uid, String userName, String password) {
        // 获取摄像机列表
        String cameraInfo = passThrough(uid, "GET /ISAPI/ContentMgmt/InputProxy/channels");
        if (StrUtil.isNotBlank(cameraInfo)) {
            Document doc;
            try {
                doc = DocumentHelper.parseText(cameraInfo);
            } catch (DocumentException e) {
                return new ArrayList<>();
            }
            JSONObject json = new JSONObject();
            Xml2JsonUtil.dom4j2Json(doc.getRootElement(), json);

            List<JSONObject> list = new ArrayList<>();
            // NVR下挂单个摄像机返回JSONObject,多个摄像机返回JSONObject数组
            Object inputProxyChannel = json.get("InputProxyChannel");
            if (inputProxyChannel instanceof JSONObject) {
                list.add((JSONObject) inputProxyChannel);
            } else {
                list = (List<JSONObject>) json.get("InputProxyChannel");
            }

            // 定义返回相机列表
            List<Camera> cameraList = new ArrayList<>();

            // DVR起始通道号 NET_DVR_DEVICEINFO_V30.byStartDChan
//            byte startDChan = DVR_INFO_V40.struDeviceV30.byStartDChan;

            // 通道号说明：一般IPC/IPD通道号为1，32路以及以下路数的NVR的IP通道通道号从33开始，64路及以上路数的NVR的IP通道通道号从1开始。
            byte startDChan = 33;
            // 最大通道个数
            HCNetSDK.NET_DVR_DEVICEINFO_V40 DVR_INFO_V40 = DVR_INFO_MAP.get(ip);
            byte byIPChanNum = DVR_INFO_V40.struDeviceV30.byIPChanNum;
            if (byIPChanNum >= 64) {
                startDChan = 1;
            }
//            log.info("---------DVR起始通道号:{}-------", startDChan);

            for (JSONObject obj : list) {
                Camera camera = new Camera();
                camera.setId(Integer.parseInt(String.valueOf(obj.get("id"))));
                camera.setName(String.valueOf(obj.get("name")));
                JSONObject descriptor = (JSONObject) obj.get("sourceInputPortDescriptor");
                if (null != descriptor) {
                    camera.setIp(String.valueOf(descriptor.get("ipAddress")));
                    camera.setPort(Integer.parseInt(String.valueOf(descriptor.get("managePortNo"))));
                }
                // 通道号
                // 通道号计算方法：DVR起始通道号(NET_DVR_DEVICEINFO_V30.byStartDChan ) + id - 1
                camera.setChannel(String.valueOf(startDChan + camera.getId() - 1));
                // 视频流url
                StringBuilder sb = new StringBuilder();
                // 为了避免受服务器CPU以及带宽的影响导致视频播放问题,这里统一采用子码流地址播放(02)
                sb.append("rtsp://").append(userName).append(":").append(password).append("@").append(ip)
                        .append(":554/Streaming/Channels/").append(camera.getId()).append("02");
                camera.setUrl(sb.toString());
                cameraList.add(camera);
            }

            // 查询状态
            String cameraStatusInfo = passThrough(uid, "GET /ISAPI/ContentMgmt/InputProxy/channels/status");
            if (StrUtil.isNotBlank(cameraStatusInfo)) {
                Document doc2 = null;
                try {
                    doc2 = DocumentHelper.parseText(cameraStatusInfo);
                } catch (DocumentException e) {
                    return cameraList;
                }
                JSONObject json2 = new JSONObject();
                Xml2JsonUtil.dom4j2Json(doc2.getRootElement(), json2);

                List<JSONObject> list2 = new ArrayList<>();
                // NVR下挂单个摄像机返回JSONObject,多个摄像机返回JSONObject数组
                Object inputProxyChannelStatus = json2.get("InputProxyChannelStatus");
                if (inputProxyChannelStatus instanceof JSONObject) {
                    list2.add((JSONObject) inputProxyChannelStatus);
                } else {
                    list2 = (List<JSONObject>) json2.get("InputProxyChannelStatus");
                }
                Map<Integer, String> map = new HashMap<>(16);
                for (JSONObject jsonObject : list2) {
                    Integer id = Integer.parseInt(String.valueOf(jsonObject.get("id")));
                    String online = String.valueOf(jsonObject.get("online"));
                    map.put(id, online);
                }

                cameraList.forEach(camera -> {
                    String status = map.get(camera.getId());
                    if (StrUtil.isNotBlank(status)) {
                        if (StrUtil.equals(status, "true")) {
                            camera.setStatus("1");
                        }
                        if (StrUtil.equals(status, "false")) {
                            camera.setStatus("2");
                        }
                    }
                });
            }
            return cameraList;
        }
        return new ArrayList<>();
    }

    /**
     * 云台控制
     *
     * @param uid          用户ID
     * @param iChannel     通道号
     * @param dwPTZCommand 云台控制命令
     * @param dwStop       是否停止 0：开启，1：停止
     * @param speed        云台控制的速度，用户按不同解码器的速度控制值设置。取值范围[1,7]
     * @return boolean
     */
    public static String cloudPlatformControl(long uid, Integer iChannel, Integer dwPTZCommand, Integer dwStop, Integer speed) {
        // dwStop: 0-开启 1-停止
        boolean bool1 = HC_NET_SDK.NET_DVR_PTZControlWithSpeed_Other((int) uid, iChannel, dwPTZCommand, dwStop, speed);
        if (!bool1) {
            log.error("云台控制失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            if (23 == HC_NET_SDK.NET_DVR_GetLastError()) {
                return "设备不支持";
            }
            throw new MediaException("云台控制失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        return "";
    }


    /**
     * 设置预置位
     *
     * @param uid            用户ID
     * @param iChannel       通道号
     * @param dwPTZPresetCmd 操作云台预置点命令
     * @param dwPresetIndex  预置点的序号（从1开始），最多支持300个预置点
     * @return boolean
     */
    public static boolean setPresetLocation(long uid, Integer iChannel, Integer dwPTZPresetCmd, Integer dwPresetIndex) {
        boolean preset = HC_NET_SDK.NET_DVR_PTZPreset_Other((int) uid, iChannel, dwPTZPresetCmd, dwPresetIndex);
        if (!preset) {
            log.error("预置点位操作错误,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("预置点位操作错误,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        return preset;
    }

    /**
     * 录像起止时间查询
     *
     * @param uid     用户ID
     * @param channel 通道号
     * @return JSONObject
     */
    public static JSONObject searchVideoTime(long uid, Integer channel) {
        HCNetSDK.NET_DVR_RECORD_TIME_SPAN_INQUIRY struRecInq = new HCNetSDK.NET_DVR_RECORD_TIME_SPAN_INQUIRY();
        struRecInq.read();
        struRecInq.dwSize = struRecInq.size();
        struRecInq.byType = 0;
        struRecInq.write();
        HCNetSDK.NET_DVR_RECORD_TIME_SPAN struRecSpan = new HCNetSDK.NET_DVR_RECORD_TIME_SPAN();
        if (!HC_NET_SDK.NET_DVR_InquiryRecordTimeSpan((int) uid, channel, struRecInq, struRecSpan)) {
            log.info("录像起止时间查询失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("录像起止时间查询失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        struRecSpan.read();
        JSONObject jsonObject = new JSONObject();

        Calendar startCalendar = Calendar.getInstance();
        startCalendar.set(struRecSpan.strBeginTime.dwYear, struRecSpan.strBeginTime.dwMonth - 1, struRecSpan.strBeginTime.dwDay,
                struRecSpan.strBeginTime.dwHour, struRecSpan.strBeginTime.dwMinute, struRecSpan.strBeginTime.dwSecond);
        Date startDate = startCalendar.getTime();

        Calendar endCalendar = Calendar.getInstance();
        endCalendar.set(struRecSpan.strEndTime.dwYear, struRecSpan.strEndTime.dwMonth - 1, struRecSpan.strEndTime.dwDay,
                struRecSpan.strEndTime.dwHour, struRecSpan.strEndTime.dwMinute, struRecSpan.strEndTime.dwSecond);
        Date endDate = endCalendar.getTime();

        jsonObject.put("startDate", DateUtil.format(startDate, "yyyy-MM-dd HH:mm:ss"));
        jsonObject.put("endDate", DateUtil.format(endDate, "yyyy-MM-dd HH:mm:ss"));
        return jsonObject;
    }

    /**
     * 录像月历查询
     *
     * @param uid     用户ID
     * @param channel 通道号
     * @param year    年
     * @param month   月
     */
    public static JSONArray getVideoMonth(long uid, int channel, int year, int month) {

        JSONArray jsonArray = new JSONArray();

        HCNetSDK.NET_DVR_MRD_SEARCH_PARAM searchParam = new HCNetSDK.NET_DVR_MRD_SEARCH_PARAM();
        searchParam.read();
        searchParam.dwSize = searchParam.size();
        searchParam.wYear = (short) year;
        searchParam.byMonth = (byte) month;
        // 通道号说明：一般IPC/IPD通道号为1，32路以及以下路数的NVR的IP通道通道号从33开始，64路及以上路数的NVR的IP通道通道号从1开始。
        searchParam.struStreamInfo.dwChannel = channel;
        searchParam.write();
        HCNetSDK.NET_DVR_MRD_SEARCH_RESULT struMrdSeaResu = new HCNetSDK.NET_DVR_MRD_SEARCH_RESULT();
        struMrdSeaResu.read();
        struMrdSeaResu.dwSize = struMrdSeaResu.size();
        struMrdSeaResu.write();
        IntByReference list = new IntByReference(0);
        boolean bool = HC_NET_SDK.NET_DVR_GetDeviceConfig((int) uid, HCNetSDK.NET_DVR_GET_MONTHLY_RECORD_DISTRIBUTION, 0, searchParam.getPointer(),
                searchParam.size(), list.getPointer(), struMrdSeaResu.getPointer(), struMrdSeaResu.size());
        if (!bool) {
            log.error("月历录像查询失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(), HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("月历录像查询失败，错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        struMrdSeaResu.read();
        for (int i = 0; i < 31; i++) {
            int day = i + 1;
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(String.valueOf(day), struMrdSeaResu.byRecordDistribution[i]);
            jsonArray.add(jsonObject);
        }
        return jsonArray;
    }

    /**
     * 录像回放码流地址获取
     *
     * @param ip        DVR的IP
     * @param userName  用户名
     * @param passWord  密码
     * @param id        相机ID
     * @param startTime 录像开始时间
     * @param endTime   录像结束时间
     * @return 录像回放码流地址
     */
    public static String getPlayBackAddress(String ip, String userName, String passWord, String id, Date startTime, Date endTime) {

        StringBuilder address = new StringBuilder();
        if (null == startTime) {
            log.error("请输入录像回放开始时间");
            return "";
        }
        address.append("rtsp://")
                .append(userName)
                .append(":")
                .append(passWord)
                .append("@")
                .append(ip)
                .append(":554")
                .append("/Streaming/tracks/")
                .append(id)
                .append("01")
                .append("?starttime=")
                .append(DateUtil.format(startTime, "yyyyMMdd't'HHmmss'z'"));
        if (null != endTime) {
            address.append("&endtime=")
                    .append(DateUtil.format(endTime, "yyyyMMdd't'HHmmss'z'"));
        }
        return address.toString();
    }

    /**
     * 录像文件查询
     *
     * @param uid       用户ID
     * @param channel   通道号
     * @param startTime 录像开始时间
     * @param endTime   录像结束时间
     * @return JSONObject
     */
    public static JSONObject findVideoFile(long uid, int channel, Date startTime, Date endTime) {

        HCNetSDK.NET_DVR_FILECOND fileCond = new HCNetSDK.NET_DVR_FILECOND();
        fileCond.struStartTime = new HCNetSDK.NET_DVR_TIME();
        fileCond.struStopTime = new HCNetSDK.NET_DVR_TIME();
        // 开始时间
        fileCond.struStartTime.dwYear = DateUtil.year(startTime);
        fileCond.struStartTime.dwMonth = DateUtil.month(startTime) + 1;
        fileCond.struStartTime.dwDay = DateUtil.dayOfMonth(startTime);
        fileCond.struStartTime.dwHour = DateUtil.hour(startTime, true);
        fileCond.struStartTime.dwMinute = DateUtil.minute(startTime);
        fileCond.struStartTime.dwSecond = DateUtil.second(startTime);
        // 结束时间
        fileCond.struStopTime.dwYear = DateUtil.year(endTime);
        fileCond.struStopTime.dwMonth = DateUtil.month(endTime) + 1;
        fileCond.struStopTime.dwDay = DateUtil.dayOfMonth(endTime);
        fileCond.struStopTime.dwHour = DateUtil.hour(endTime, true);
        fileCond.struStopTime.dwMinute = DateUtil.minute(endTime);
        fileCond.struStopTime.dwSecond = DateUtil.second(endTime);

        // 通道号
        fileCond.lChannel = channel;
        // 文件类型
        fileCond.dwFileType = 0xff;
        fileCond.dwIsLocked = 0xff;

        int fileInt = HC_NET_SDK.NET_DVR_FindFile_V30((int) uid, fileCond);
        if (-1 == fileInt) {
            log.error("查询录像文件失败,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("查询录像文件失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }

        HCNetSDK.NET_DVR_FINDDATA_V30 strFile = new HCNetSDK.NET_DVR_FINDDATA_V30();
        // 总录像时长
        long sumVideoTime = 0;
        int lnext;
        // 定义返回值
        JSONObject jsonObject = new JSONObject();
        // 视频详情
        List<VideoInfo> videoInfoList = new ArrayList<>();

        while (true) {
            lnext = HC_NET_SDK.NET_DVR_FindNextFile_V30(fileInt, strFile);
            if (lnext == HCNetSDK.NET_DVR_FILE_SUCCESS) {
                String start = strFile.struStartTime.toStringTime();
                String end = strFile.struStopTime.toStringTime();

                int iTemp;
                String videoSize;
                if (strFile.dwFileSize < 1024 * 1024) {
                    iTemp = (strFile.dwFileSize) / (1024);
                    videoSize = iTemp + "K";
                } else {
                    iTemp = (strFile.dwFileSize) / (1024 * 1024);
                    videoSize = iTemp + "M   ";
                    iTemp = ((strFile.dwFileSize) % (1024 * 1024)) / (1204);
                    videoSize = videoSize + iTemp + "K";
                }

                // 添加文件名信息
                String[] s;
                s = new String(strFile.sFileName).split("\0", 2);

                DateTime startDate = DateUtil.parse(start, "yyyy/MM/ddHH:mm:ss");
                DateTime endDate = DateUtil.parse(end, "yyyy/MM/ddHH:mm:ss");

                // 录像时长
                long videoTime = DateUtil.between(endDate, startDate, DateUnit.MINUTE);

                VideoInfo videoInfo = new VideoInfo();
                videoInfo.setVideoName(s[0]);
                videoInfo.setStartTime(startDate);
                videoInfo.setEndTime(endDate);
                videoInfo.setVideoSize(videoSize);
                videoInfo.setVideoTime(videoTime);
                videoInfo.setVideoUrl("/media/data/images/video/" + s[0] + ".avi");
                videoInfoList.add(videoInfo);

                sumVideoTime = sumVideoTime + videoTime;
            } else {
                if (lnext == HCNetSDK.NET_DVR_ISFINDING) {
                } else {
                    if (lnext == HCNetSDK.NET_DVR_FILE_NOFIND) {
                        log.info("没有搜到文件");
                        return jsonObject;
                    } else {
//                        log.info("搜索文件结束");
                        HC_NET_SDK.NET_DVR_FindClose_V30(fileInt);
                        jsonObject.put("videoInfoList", videoInfoList);
                        jsonObject.put("sumVideoTime", sumVideoTime);
                        return jsonObject;
                    }
                }
            }
        }
    }

    /**
     * 抓取图片
     *
     * @param uid         用户ID
     * @param channel     通道号
     * @param saveAddress 保存地址
     */
    public static String captureJpeg(long uid, int channel, String saveAddress, Integer quality) {
        // 图片名
        String picName = saveAddress + System.currentTimeMillis() + ".jpg";
        HCNetSDK.NET_DVR_JPEGPARA jpegpara = new HCNetSDK.NET_DVR_JPEGPARA();
        // 画质不传，默认是1-较好
        if (null == quality) {
            jpegpara.wPicQuality = 1;
        } else {
            jpegpara.wPicQuality = quality.shortValue();
        }
        jpegpara.wPicSize = 0xff;
        IntByReference reference = new IntByReference();
        // 设置图片大小
        ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
        long sTime = System.currentTimeMillis();
        boolean capture = HC_NET_SDK.NET_DVR_CaptureJPEGPicture_NEW((int) uid, channel, jpegpara, jpegBuffer, 1024 * 1024, reference);
        if (!capture) {
            log.error("截图失败,请确认相机状态,错误码:{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("抓图失败,失败原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        log.info("截图成功,返回长度：{}", reference.getValue());
        if (reference.getValue() > 0) {
            // 创建文件夹
            FileUtil.mkdir(saveAddress);
            File file = new File(picName);
            try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file))) {
                outputStream.write(jpegBuffer.array(), 0, reference.getValue());
                outputStream.flush();
                log.info("文件写入成功,耗费时间：{}s", (System.currentTimeMillis() - sTime) / 1000);
                return picName;
            } catch (Exception e) {
                FileUtil.del(new File(picName));
                throw new MediaException("抓图后写入文件失败");
            }
        } else {
            return "";
        }
    }

    /**
     * 开始录像
     *
     * @param uid     用户ID
     * @param channel 通道号
     * @return 视频预览返回值
     */
    public static long startMakeVideo(long uid, int channel, String saveAddress) {
        HCNetSDK.NET_DVR_PREVIEWINFO previewinfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        previewinfo.lChannel = channel;
        previewinfo.bBlocked = 1;
        previewinfo.byProtoType = 0;
        previewinfo.dwDisplayBufNum = 1;
//        previewinfo.hPlayWnd = null;
        previewinfo.hPlayWnd = 0;
        previewinfo.write();

        int lPreviewHandle = HC_NET_SDK.NET_DVR_RealPlay_V40((int) uid, previewinfo, null, null);
        if (lPreviewHandle == -1) {
            log.error("实时预览出现错误，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            return lPreviewHandle;
        } else {
            log.info("实时预览成功");
            log.info("录像中...");
        }

        if (!HC_NET_SDK.NET_DVR_SaveRealData_V30(lPreviewHandle, 2, saveAddress)) {
            log.error("保存录像失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("保存录像失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        return lPreviewHandle;
    }

    /**
     * 停止录像
     *
     * @param lPreviewHandle 实时预览返回值
     */
    public static void stopMakeVideo(long lPreviewHandle) {
        if (!HC_NET_SDK.NET_DVR_StopSaveRealData((int) lPreviewHandle)) {
            log.error("停止录像失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("停止录像失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            log.info("停止录像成功");
        }

        if (!HC_NET_SDK.NET_DVR_StopRealPlay((int) lPreviewHandle)) {
            log.error("停止预览失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("停止预览失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            log.info("停止预览成功");
        }
    }

    /**
     * 录像文件下载(按时间)
     *
     * @param uid         用户ID
     * @param channel     通道号
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param saveAddress 保存地址
     */
    public static long downloadVideoFileByTime(int uid, int channel, String fileName, Date startTime, Date endTime, String saveAddress) {

        log.info(fileName + "下载启动。。。");

        HCNetSDK.NET_DVR_PLAYCOND struPlayCond = new HCNetSDK.NET_DVR_PLAYCOND();
        struPlayCond.dwChannel = channel;

        // 开始时间
        struPlayCond.struStartTime.dwYear = DateUtil.year(startTime);
        struPlayCond.struStartTime.dwMonth = DateUtil.month(startTime) + 1;
        struPlayCond.struStartTime.dwDay = DateUtil.dayOfMonth(startTime);
        struPlayCond.struStartTime.dwHour = DateUtil.hour(startTime, true);
        struPlayCond.struStartTime.dwMinute = DateUtil.minute(startTime);
        struPlayCond.struStartTime.dwSecond = DateUtil.second(startTime);
        // 结束时间
        struPlayCond.struStopTime.dwYear = DateUtil.year(endTime);
        struPlayCond.struStopTime.dwMonth = DateUtil.month(endTime) + 1;
        struPlayCond.struStopTime.dwDay = DateUtil.dayOfMonth(endTime);
        struPlayCond.struStopTime.dwHour = DateUtil.hour(endTime, true);
        struPlayCond.struStopTime.dwMinute = DateUtil.minute(endTime);
        struPlayCond.struStopTime.dwSecond = DateUtil.second(endTime);

        // 码流类型：0-主码流，1-子码流，2-码流三
        struPlayCond.byStreamType = 0;
        struPlayCond.write();

        log.info("savefile保存路径：" + saveAddress);

        byte[] byFileNameCn = new byte[128];
        // 路径包含中文需要注意字符集
        try {
            System.arraycopy(saveAddress.getBytes("GBK"), 0, byFileNameCn, 0, saveAddress.getBytes("GBK").length);
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        int iDownHandle = HC_NET_SDK.NET_DVR_GetFileByTime_V40(uid, byFileNameCn, struPlayCond);
        // 缓存句柄
        handleCache.put("iDownHandle-" + fileName, iDownHandle);

        if (iDownHandle < 0) {
            log.error("录像文件下载失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("录像文件下载失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        return iDownHandle;
    }

    /**
     * 获取下载进度
     *
     * @param iDownHandle 开始下载接口的返回值
     * @return 下载进度
     */
    public static int getDownLoadProgress(long iDownHandle) {
        // 下载进度
        int iDownPos;
        IntByReference nPos = new IntByReference(0);
        HC_NET_SDK.NET_DVR_PlayBackControl_V40((int) iDownHandle, HCNetSDK.NET_DVR_PLAYGETPOS, null, 0,
                nPos.getPointer(), new IntByReference(0));
        iDownPos = nPos.getValue();
        if (iDownPos < 0) {
            int errorCode = HC_NET_SDK.NET_DVR_GetLastError();
            log.error("获取当前下载录像文件的进度失败，错误码：{},错误原因：{}", errorCode, HkErrorEnum.getErrorMsgByCode(errorCode));
            return 0;
        }
        log.info("正常下载中，进度：" + iDownPos);
        return iDownPos;
    }

    /**
     * 通过文件名下载
     *
     * @param uid         用户ID
     * @param fileName    文件名
     * @param saveAddress 保存地址
     */
    public static void downloadVideoFileByFileName(int uid, String fileName, String saveAddress) {
        int iDownHandle = HC_NET_SDK.NET_DVR_GetFileByName(uid, fileName, saveAddress);
        // 缓存句柄
        handleCache.put("iDownHandle-" + fileName, iDownHandle);
        if (iDownHandle < 0) {
            int iErr = HC_NET_SDK.NET_DVR_GetLastError();
            log.error("NET_DVR_GetFileByName失败，错误码:{},错误原因：{}", iErr, HkErrorEnum.getErrorMsgByCode(iErr));
        } else {
            if (!HC_NET_SDK.NET_DVR_PlayBackControl_V40(iDownHandle, HCNetSDK.NET_DVR_PLAYSTART, null,
                    0, null, new IntByReference(0))) {
                int iErr = HC_NET_SDK.NET_DVR_GetLastError();
                log.error("NET_DVR_PLAYSTART失败，错误码：{},错误原因：{}", iErr, HkErrorEnum.getErrorMsgByCode(iErr));
                return;
            }
            long startTime = System.currentTimeMillis();
            // 下载进度
            int iDownPos = 0;
            while (true) {
                IntByReference nPos = new IntByReference(0);
                HC_NET_SDK.NET_DVR_PlayBackControl_V40(iDownHandle, HCNetSDK.NET_DVR_PLAYGETPOS, null, 0, nPos.getPointer(), new IntByReference(0));
                iDownPos = nPos.getValue();
                if (iDownPos < 0) {
                    int iErr = HC_NET_SDK.NET_DVR_GetLastError();
                    log.error("NET_DVR_GetDownloadPos失败，错误码：{},错误原因：{}", iErr, HkErrorEnum.getErrorMsgByCode(iErr));
                    break;
                }
                if (iDownPos < 100) {
                    log.info(fileName + "正常下载中，进度：" + iDownPos);
                    continue;
                }
                if (iDownPos == 100) {
                    long endTime = System.currentTimeMillis();
                    log.info(fileName + "下载完成, 保存路径:" + saveAddress + ",耗时：" + (endTime - startTime) / 1000 + "秒");
                    break;
                }
                log.info(fileName + "下载异常，进度：" + iDownPos);
                break;
            }
            // 停止下载，释放资源
            HC_NET_SDK.NET_DVR_StopGetFile(iDownHandle);
        }
    }

    /**
     * 停止下载
     */
    public static void stopDownloadVideoFile() {
        log.info("handleCache:{}", JSON.toJSONString(handleCache));
        Iterator<String> iterator = handleCache.keySet().iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            Integer iDownHandle = handleCache.get(next);
            log.info("调用停止下载接口,下载句柄:{}", iDownHandle);
            boolean bool = HC_NET_SDK.NET_DVR_StopGetFile(iDownHandle);
            // 删除下载句柄缓存数据
            iterator.remove();
            handleCache.remove(next);
            if (!bool) {
                log.error("停止下载接口调用失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                        HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                throw new MediaException("停止下载接口调用失败,错误原因:" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            }
        }
    }

    /**
     * 测温规则
     *
     * @param param 测温规则参数
     * @throws UnsupportedEncodingException Exception
     */
    public static boolean makeTemperatureRule(TemperatureRule param) {
        // 1.获取热成像总能力集
        boolean bool1 = getTemperatureAbility(param.getUid());
        if (!bool1) {
            return false;
        }

        // 2.智能资源配置,启用测温功能，默认已启动
        passThrough(param.getUid(), "GET /ISAPI/Thermal/channels/2/thermIntell/capabilities");
        passThrough(param.getUid(), "GET /ISAPI/Thermal/channels/2/thermIntell");

        // 3.设置测温模式：专家模式
        boolean bool2 = setTemperatureModel(param.getUid(), param.getChannel());
        if (!bool2) {
            return false;
        }

        // 4.预置点设置（这里页面交互需要先设置预置点再配置规则）

        // 5.设置预置点关联测温信息
        boolean bool3 = setAssociateTemperature(param);
        if (!bool3) {
            return false;
        }
        // 6.设置测温报警规则配置数据
        boolean bool4 = setAlarmConfig(param);
        if (!bool4) {
            return false;
        }
        // 7.设置联动配置-上传中心，这样才可以通过布控的方式获取到报警信息
        boolean bool5 = setLinkageConfig(param.getUid(), param.getChannel(), param.getLocationIndex());
        if (!bool5) {
            return false;
        }
        // 8.设置测温基本参数
        boolean bool6 = setBasicParam(param.getUid(), param.getChannel());
        if (!bool6) {
            return false;
        }
        return true;
    }

    /**
     * 设置布防
     *
     * @param ip  设备ip
     * @param uid 用户ID
     */
    public static int setUpAlarm(String ip, long uid, HCNetSDK.FMSGCallBack_V31 callBackV31) {
        if (!HC_NET_SDK.NET_DVR_SetDVRMessageCallBack_V31(callBackV31, null)) {
            log.error("设置回调函数失败,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("设置回调函数失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        }
        HCNetSDK.NET_DVR_SETUPALARM_PARAM alarmParam = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
        alarmParam.dwSize = alarmParam.size();
        // 智能交通布防优先级：0- 一等级（高），1- 二等级（中），2- 三等级（低）
        alarmParam.byLevel = 1;
        // 智能交通报警信息上传类型：0- 老报警信息（NET_DVR_PLATE_RESULT），1- 新报警信息(NET_ITS_PLATE_RESULT)
        alarmParam.byAlarmInfoType = 1;
        // 布防类型(仅针对门禁主机、人证设备)：0-客户端布防(会断网续传)，1-实时布防(只上传实时数据)
        alarmParam.byDeployType = 1;
        alarmParam.write();
        // 开启布防
        int alarmHandle = HC_NET_SDK.NET_DVR_SetupAlarmChan_V41((int) uid, alarmParam);
        if (alarmHandle == -1) {
            log.error("设备：{},布防失败，错误码:{},错误原因：{}", ip, HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            log.info("设备:{},布防成功,句柄：{}", ip, alarmHandle);
        }
        return alarmHandle;
    }

    /**
     * 报警撤防
     *
     * @param alarmHandle 报警布防句柄
     */
    public static boolean closeAlarmChan(int alarmHandle) {
        log.info("撤防句柄：{}", alarmHandle);
        // 报警撤防
        if (HC_NET_SDK.NET_DVR_CloseAlarmChan_V30(alarmHandle)) {
            log.info("撤防成功！");
            return true;
        }
        log.error("撤防失败,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        return false;
    }


    /**
     * 开启实时测温功能
     *
     * @param ip  设备IP
     * @param uid 用户ID
     * @return 测温句柄
     */
    public static int setUpThermometry(String ip, long uid, HCNetSDK.FRemoteConfigCallback realTimeThermometryCallBack) {
        if (uid == -1) {
            log.info("设备" + ip + "开启实时测温失败,请先注册");
            return -1;
        }
        HCNetSDK.NET_DVR_REALTIME_THERMOMETRY_COND struRealTimeCond = new HCNetSDK.NET_DVR_REALTIME_THERMOMETRY_COND();
        struRealTimeCond.dwSize = struRealTimeCond.size();
        // 通道号,这里写死红外相机通道2
        struRealTimeCond.dwChan = 2;
        // 规则ID,0表示所有规则
        struRealTimeCond.byRuleID = 0;
        // 长连接模式：0- 保留（兼容不支持该功能的老设备），1- 定时模式，2- 温差模式
        struRealTimeCond.byMode = 1;
        struRealTimeCond.write();
        int handle = HC_NET_SDK.NET_DVR_StartRemoteConfig((int) uid, HCNetSDK.NET_DVR_GET_REALTIME_THERMOMETRY, struRealTimeCond.getPointer(),
                struRealTimeCond.size(), realTimeThermometryCallBack, null);
        if (handle == -1) {
            log.error("设备" + ip + "开启实时测温失败，错误号:" + HC_NET_SDK.NET_DVR_GetLastError());
        } else {
            log.info("设备" + ip + "开启实时测温成功,句柄：{}", handle);
        }
        return handle;
    }

    /**
     * 关闭实时测温
     *
     * @param handle 测温句柄
     */
    public static boolean closeThermometry(int handle) {
        log.info("关闭实时测温句柄：{}", handle);
        if (!HC_NET_SDK.NET_DVR_StopRemoteConfig(handle)) {
            log.info("关闭实时测温失败,错误码：{}", HC_NET_SDK.NET_DVR_GetLastError());
            return false;
        } else {
            log.info("关闭实时测温成功！");
            return true;
        }
    }

    /**
     * 透传封装
     *
     * @param uid 用户ID
     * @param url 请求地址
     * @return 接口返回数据
     */
    private static String passThrough(long uid, String url) {
        HCNetSDK.NET_DVR_XML_CONFIG_INPUT input = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
        HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT output = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT();
        input.read();
        output.read();

        input.dwSize = input.size();

        HCNetSDK.BYTE_ARRAY ptrUrl = new HCNetSDK.BYTE_ARRAY(256);
        System.arraycopy(url.getBytes(), 0, ptrUrl.byValue, 0, url.length());
        ptrUrl.write();
        input.lpRequestUrl = ptrUrl.getPointer();
        input.dwRequestUrlLen = url.length();

        input.lpInBuffer = null;
        input.dwInBufferSize = 0;
        input.write();

        output.dwSize = output.size();
        HCNetSDK.BYTE_ARRAY ptrOutByte = new HCNetSDK.BYTE_ARRAY(1024 * 1024);
        output.lpOutBuffer = ptrOutByte.getPointer();
        output.dwOutBufferSize = 1024 * 1024;
        output.write();

        if (!HC_NET_SDK.NET_DVR_STDXMLConfig((int) uid, input, output)) {
            log.error("海康透传解析出现错误,错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            return "";
        } else {
            log.info("海康透传成功");
            output.read();
            ptrOutByte.read();
            return new String(ptrOutByte.byValue).trim();
        }
    }

    /**
     * 获取热成像总能力集
     *
     * @param uid 用户ID
     */
    private static boolean getTemperatureAbility(long uid) {
        String info = passThrough(uid, "GET /ISAPI/Thermal/capabilities");
        Document doc;
        try {
            doc = DocumentHelper.parseText(info);
        } catch (DocumentException e) {
            return false;
        }

        JSONObject json = new JSONObject();
        Xml2JsonUtil.dom4j2Json(doc.getRootElement(), json);

        // 测温功能
        if (!Boolean.parseBoolean((String) json.get("isSupportThermometry"))) {
            log.error("设备不支持测温功能！");
            throw new MediaException("测温规则保存失败，原因：设备不支持测温功能！");
        }
        // 智能资源分配
        if (!Boolean.parseBoolean((String) json.get("isSupportThermIntell"))) {
            log.error("设备不支持智能资源分配！");
            throw new MediaException("测温规则保存失败，原因：设备不支持智能资源分配！");
        }
        // 测温模式配置
        if (!Boolean.parseBoolean((String) json.get("isSupportThermometryMode"))) {
            log.error("设备不支持测温模式配置！");
            throw new MediaException("测温规则保存失败，原因：设备不支持测温模式配置！");
        }
        return true;
    }

    /**
     * 设置测温模式-专家模式
     *
     * @param uid     用户ID
     * @param channel 通道号
     * @return boolean
     */
    private static boolean setTemperatureModel(long uid, int channel) {
        // 先GET
        IntByReference pchannel = new IntByReference(channel);
        Pointer pChannelNum = pchannel.getPointer();

        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_MODE thermomeTryMode = new HCNetSDK.NET_DVR_THERMOMETRY_MODE();

        stdConfig.lpCondBuffer = pChannelNum;
        stdConfig.dwCondSize = 4;
        stdConfig.lpInBuffer = null;
        stdConfig.dwInSize = 0;
        stdConfig.lpOutBuffer = thermomeTryMode.getPointer();
        stdConfig.dwOutSize = thermomeTryMode.size();
        stdConfig.write();
        thermomeTryMode.write();

        if (!HC_NET_SDK.NET_DVR_GetSTDConfig((int) uid, 6765, stdConfig)) {
            log.error("获取测温模式参数失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("获取测温模式参数失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            thermomeTryMode.read();
            log.info("测温模式：" + thermomeTryMode.byMode);
            // 再SET
            stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
            stdConfig.lpCondBuffer = pChannelNum;
            stdConfig.dwCondSize = 4;
            thermomeTryMode.dwSize = thermomeTryMode.size();
            // 测温模式：0- 普通模式，1- 专家模式
            thermomeTryMode.byMode = 1;
            thermomeTryMode.byThermometryROIEnabled = 0;
            stdConfig.lpInBuffer = thermomeTryMode.getPointer();
            stdConfig.dwInSize = thermomeTryMode.dwSize;
            thermomeTryMode.write();
            stdConfig.write();
            if (!HC_NET_SDK.NET_DVR_SetSTDConfig((int) uid, 6766, stdConfig)) {
                log.error("测温模式设置失败！！! 错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                        HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                throw new MediaException("测温规则保存失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            } else {
                log.info("测温模式设置成功！");
                return true;
            }
        }
    }

    /**
     * 获取测温模式
     *
     * @param uid     用户ID
     * @param channel 通道号
     */
    private static void getTemperatureModel(int uid, int channel) {
        IntByReference pchannel = new IntByReference(channel);
        Pointer pChannelNum = pchannel.getPointer();

        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_MODE thermometryMode = new HCNetSDK.NET_DVR_THERMOMETRY_MODE();

        stdConfig.lpCondBuffer = pChannelNum;
        stdConfig.dwCondSize = 4;
        stdConfig.lpInBuffer = null;
        stdConfig.dwInSize = 0;
        stdConfig.lpOutBuffer = thermometryMode.getPointer();
        stdConfig.dwOutSize = thermometryMode.size();
        stdConfig.write();
        thermometryMode.write();

        if (!HC_NET_SDK.NET_DVR_GetSTDConfig(uid, 6765, stdConfig)) {
            log.error("获取测温模式参数失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("获取测温模式参数失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            thermometryMode.read();
            log.info("测温模式：" + thermometryMode.byMode);
        }
    }

    /**
     * 设置预置点关联测温信息
     *
     * @param param 测温规则配置参数
     * @return boolean
     * @throws UnsupportedEncodingException Exception
     */
    private static boolean setAssociateTemperature(TemperatureRule param) {
        // 先GET
        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_COND cond = new HCNetSDK.NET_DVR_THERMOMETRY_COND();
        HCNetSDK.NET_DVR_THERMOMETRY_PRESETINFO presetInfo = new HCNetSDK.NET_DVR_THERMOMETRY_PRESETINFO();
        cond.dwSize = cond.size();
        cond.dwChannel = param.getChannel();
        // 预置点位号
        cond.wPresetNo = (short) param.getLocationIndex();
        cond.write();
        stdConfig.lpCondBuffer = cond.getPointer();
        stdConfig.dwCondSize = cond.dwSize;
        stdConfig.lpOutBuffer = presetInfo.getPointer();
        stdConfig.dwOutSize = presetInfo.size();
        stdConfig.byDataType = 0;
        presetInfo.write();
        stdConfig.write();
        boolean bRet = HC_NET_SDK.NET_DVR_GetSTDConfig((int) param.getUid(), 3624, stdConfig);
        if (!bRet) {
            log.error("预置点参数获取失败！！! 错误码:{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("预置点参数获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            presetInfo.read();
            // 再SET
            stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
            stdConfig.lpCondBuffer = cond.getPointer();
            stdConfig.dwCondSize = cond.dwSize;
            HCNetSDK.BYTE_ARRAY byteArray = new HCNetSDK.BYTE_ARRAY(4096 * 4);
            stdConfig.lpStatusBuffer = byteArray.getPointer();
            stdConfig.dwStatusSize = 4096 * 4;
            presetInfo.dwSize = presetInfo.size();
            // 预置点号
            presetInfo.wPresetNo = (short) param.getLocationIndex();
            // 规则ID
            int ruleId = param.getRuleId();
            // 测温规则数组下标 = 规则ID-1
            int presetInfoIndex = ruleId - 1;

            presetInfo.struPresetInfo[presetInfoIndex] = new HCNetSDK.NET_DVR_THERMOMETRY_PRESETINFO_PARAM();

            // byEnabled 是否使能：0- 否，1- 是
            presetInfo.struPresetInfo[presetInfoIndex].byEnabled = (byte) param.getRuleEnable();
            // byRuleID 规则ID，从1开始（list内部判断数据有效性），0表示无效
            presetInfo.struPresetInfo[presetInfoIndex].byRuleID = (byte) param.getRuleId();
            // wDistance 距离，单位：m，取值范围：[0, 10000]
            presetInfo.struPresetInfo[presetInfoIndex].wDistance = (short) param.getDistance();
            // fEmissivity 发射率
            presetInfo.struPresetInfo[presetInfoIndex].fEmissivity = param.getfEmissivity();
            // byDistanceUnit 距离单位: 0- 米(m)，1- 英尺(feet)，2-厘米（cm）
            presetInfo.struPresetInfo[presetInfoIndex].byDistanceUnit = 0;
            // byReflectiveEnabled 反射温度使能：0- 否，1- 是
            presetInfo.struPresetInfo[presetInfoIndex].byReflectiveEnabled = (byte) param.getReflectiveEnable();
            // fReflectiveTemperature 反射温度，精确到小数后一位
            presetInfo.struPresetInfo[presetInfoIndex].fReflectiveTemperature = param.getfReflectiveTemperature();
            // szRuleName 规则名称
            byte[] ruleName = new byte[0];
            try {
                ruleName = param.getRuleName().getBytes("GBK");
            } catch (UnsupportedEncodingException e) {
                log.error("规则名称字符集转换失败");
            }
            System.arraycopy(ruleName, 0, presetInfo.struPresetInfo[presetInfoIndex].szRuleName, 0, ruleName.length);
            // 规则标定类型：0- 点，1- 框，2- 线
            presetInfo.struPresetInfo[presetInfoIndex].byRuleCalibType = (byte) param.getRuleType();

            int ruleType = param.getRuleType();
            if (0 == ruleType) {
                // 点测温
                presetInfo.struPresetInfo[presetInfoIndex].struPoint.fX = param.getPointParam().getfX();
                presetInfo.struPresetInfo[presetInfoIndex].struPoint.fY = param.getPointParam().getfY();
            }
            if (1 == ruleType) {
                // 框测温
                RegionParam regionParam = param.getRegionParam();
                List<PointParam> paramList = regionParam.getPointParamList();

                // NET_VCA_POLYGON 多边形结构体
                presetInfo.struPresetInfo[presetInfoIndex].struRegion = new HCNetSDK.NET_VCA_POLYGON();
                // dwPointNum 有效点（大于等于3），若是3点在一条线上认为是无效区域，线交叉认为是无效区域
                presetInfo.struPresetInfo[presetInfoIndex].struRegion.dwPointNum = regionParam.getDwPointNum();

                for (int i = 0; i < paramList.size(); i++) {
                    PointParam par = paramList.get(i);
                    // struPos[] 多边形边界点，最大值为10
                    // NET_VCA_POINT 点坐标参数结构体
                    presetInfo.struPresetInfo[presetInfoIndex].struRegion.struPos[i] = new HCNetSDK.NET_VCA_POINT();
                    // fX X轴坐标，取值范围[0.001,1]
                    presetInfo.struPresetInfo[presetInfoIndex].struRegion.struPos[i].fX = par.getfX();
                    // fY Y轴坐标，取值范围[0.001,1]
                    presetInfo.struPresetInfo[presetInfoIndex].struRegion.struPos[i].fY = par.getfY();
                    presetInfo.struPresetInfo[presetInfoIndex].struRegion.struPos[i].write();
                }
            }
            presetInfo.write();
            stdConfig.lpInBuffer = presetInfo.getPointer();
            stdConfig.dwInSize = presetInfo.dwSize;

            stdConfig.byDataType = 0;
            stdConfig.write();

            boolean bRet1 = HC_NET_SDK.NET_DVR_SetSTDConfig((int) param.getUid(), 3625, stdConfig);
            if (!bRet1) {
                log.error("预置点:{},参数设置失败！！! 错误码：{},错误原因：{}", param.getLocationIndex(),
                        HC_NET_SDK.NET_DVR_GetLastError(), HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                throw new MediaException("预置点参数获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            } else {
                log.info("预置点:{},参数设置成功", param.getLocationIndex());
                return true;
            }
        }
    }

    /**
     * 获取预置点关联测温规则
     *
     * @param uid           用户ID
     * @param channel       通道号
     * @param locationIndex 预置点位置
     * @return NET_DVR_STD_CONFIG
     * @throws UnsupportedEncodingException 异常
     */
    private static void getAssociateTemperature(int uid, int channel, int locationIndex) throws
            UnsupportedEncodingException {
        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_COND cond = new HCNetSDK.NET_DVR_THERMOMETRY_COND();
        HCNetSDK.NET_DVR_THERMOMETRY_PRESETINFO presetInfo = new HCNetSDK.NET_DVR_THERMOMETRY_PRESETINFO();
        cond.dwSize = cond.size();
        cond.dwChannel = channel;
        // 预置点位号
        cond.wPresetNo = (short) locationIndex;
        cond.write();
        stdConfig.lpCondBuffer = cond.getPointer();
        stdConfig.dwCondSize = cond.dwSize;
        stdConfig.lpOutBuffer = presetInfo.getPointer();
        stdConfig.dwOutSize = presetInfo.size();
        stdConfig.byDataType = 0;
        presetInfo.write();
        stdConfig.write();
        boolean bRet = HC_NET_SDK.NET_DVR_GetSTDConfig(uid, 3624, stdConfig);
        if (!bRet) {
            log.error("预置点参数获取失败！！! 错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("预置点参数获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            presetInfo.read();
            HCNetSDK.NET_DVR_THERMOMETRY_PRESETINFO_PARAM[] presetInfoArr = presetInfo.struPresetInfo;
            for (int i = 0; i < presetInfoArr.length; i++) {
                log.info("预置点参数获取成功!预置点：" + presetInfo.wPresetNo
                        + "，使能：" + presetInfoArr[i].byEnabled
                        + ",规则ID：" + presetInfoArr[i].byRuleID
                        + ",距离：" + presetInfoArr[i].wDistance
                        + ",发射率：" + presetInfoArr[i].fEmissivity
                        + ",距离单位：" + presetInfoArr[i].byDistanceUnit
                        + ",反射温度使能：" + presetInfoArr[i].byReflectiveEnabled
                        + ",反射温度：" + presetInfoArr[i].fReflectiveTemperature
                        + ",规则名称：" + new String(presetInfoArr[i].szRuleName, "GBK").trim()
                        + ",规则标定类型：" + presetInfoArr[i].byRuleCalibType
                        + ",框测温坐标：（" + presetInfoArr[i].struRegion.struPos[0].fX + "," + presetInfoArr[i].struRegion.struPos[0].fY + ")"
                        + "（" + presetInfoArr[i].struRegion.struPos[1].fX + "," + presetInfoArr[i].struRegion.struPos[1].fY + ")"
                        + "（" + presetInfoArr[i].struRegion.struPos[2].fX + "," + presetInfoArr[i].struRegion.struPos[2].fY + ")"
                        + "（" + presetInfoArr[i].struRegion.struPos[3].fX + "," + presetInfoArr[i].struRegion.struPos[3].fY + ")");
            }
        }
    }

    /**
     * 设置测温报警规则配置数据
     *
     * @param rule 测温规则配置参数
     */
    private static boolean setAlarmConfig(TemperatureRule rule) {
        // 先GET
        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_COND cond = new HCNetSDK.NET_DVR_THERMOMETRY_COND();
        HCNetSDK.NET_DVR_THERMOMETRY_ALARMRULE alarmRule = new HCNetSDK.NET_DVR_THERMOMETRY_ALARMRULE();

        cond.dwSize = cond.size();
        cond.dwChannel = rule.getChannel();
        // 预置点位号
        cond.wPresetNo = (short) rule.getLocationIndex();
        cond.write();

        stdConfig.lpCondBuffer = cond.getPointer();
        stdConfig.dwCondSize = cond.dwSize;

        stdConfig.lpOutBuffer = alarmRule.getPointer();
        stdConfig.dwOutSize = alarmRule.size();
        stdConfig.byDataType = 0;
        alarmRule.write();
        stdConfig.write();

        boolean bool = HC_NET_SDK.NET_DVR_GetSTDConfig((int) rule.getUid(), 3627, stdConfig);
        if (!bool) {
            log.error("测温报警规则配置数据获取失败！！! 错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("测温报警规则配置数据获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            alarmRule.read();
            // 再SET
            stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
            stdConfig.lpCondBuffer = cond.getPointer();
            stdConfig.dwCondSize = cond.dwSize;
            HCNetSDK.BYTE_ARRAY byteArray = new HCNetSDK.BYTE_ARRAY(4096 * 4);
            stdConfig.lpStatusBuffer = byteArray.getPointer();
            stdConfig.dwStatusSize = 4096 * 4;
            alarmRule.dwSize = alarmRule.size();
            // 规则ID
            int ruleId = rule.getRuleId();
            // 报警信息数组下标 = 规则ID-1
            int ruleParamArrIndex = ruleId - 1;

            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex] = new HCNetSDK.NET_DVR_THERMOMETRY_ALARMRULE_PARAM();
            // byEnabled 是否使能：0- 否，1- 是
            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex].byEnabled = 1;
            // byRuleID 规则ID，从1开始（list内部判断数据有效性），0表示无效
            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex].byRuleID = (byte) rule.getRuleId();
            // byRule 报警温度比较方式：0- 高温大于，1- 高温小于，2- 低温大于，3- 低温小于，4- 平均温大于，5- 平均温小于，6- 温差大于，7- 温差小于
            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex].byRule = (byte) rule.getAlarmRule();
            // fAlarm 报警温度
            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex].fAlarm = rule.getAlarmTemperature();
            // fAlert 预警温度
            // TODO 暂定 预警温度=报警温度
            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex].fAlert = rule.getAlarmTemperature();
            // fThreshold 门限温度（测温的浮动范围，例如设置报警温度为60度，门限温度为5度，则在55到65度之间都会报警）
            alarmRule.struThermometryAlarmRuleParam[ruleParamArrIndex].fThreshold = 1;
            alarmRule.write();

            stdConfig.lpInBuffer = alarmRule.getPointer();
            stdConfig.dwInSize = alarmRule.dwSize;
            stdConfig.byDataType = 0;
            stdConfig.write();

            bool = HC_NET_SDK.NET_DVR_SetSTDConfig((int) rule.getUid(), 3628, stdConfig);
            if (!bool) {
                log.error("预置点:{},测温报警规则设置失败！！! 错误码：{},错误原因：{}", rule.getLocationIndex(),
                        HC_NET_SDK.NET_DVR_GetLastError(), HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                throw new MediaException("测温报警规则设置失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            } else {
                log.info("预置点:{},测温报警规则设置成功", rule.getLocationIndex());
                return true;
            }
        }
    }

    /**
     * 获取测温报警规则配置数据
     *
     * @param uid           用户ID
     * @param channel       通道号
     * @param locationIndex 预置位号
     */
    private static void getAlarmConfig(int uid, int channel, int locationIndex) {
        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_COND cond = new HCNetSDK.NET_DVR_THERMOMETRY_COND();
        HCNetSDK.NET_DVR_THERMOMETRY_ALARMRULE alarmRule = new HCNetSDK.NET_DVR_THERMOMETRY_ALARMRULE();

        cond.dwSize = cond.size();
        cond.dwChannel = channel;
        // 预置点位号
        cond.wPresetNo = (short) locationIndex;
        cond.write();

        stdConfig.lpCondBuffer = cond.getPointer();
        stdConfig.dwCondSize = cond.dwSize;

        stdConfig.lpOutBuffer = alarmRule.getPointer();
        stdConfig.dwOutSize = alarmRule.size();
        stdConfig.byDataType = 0;
        alarmRule.write();
        stdConfig.write();

        boolean bool = HC_NET_SDK.NET_DVR_GetSTDConfig(uid, 3627, stdConfig);
        if (!bool) {
            log.error("测温报警规则配置数据获取失败！！! 错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("测温报警规则配置数据获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            alarmRule.read();
            HCNetSDK.NET_DVR_THERMOMETRY_ALARMRULE_PARAM[] ruleParamArr = alarmRule.struThermometryAlarmRuleParam;
            for (int i = 0; i < ruleParamArr.length; i++) {
                try {
                    log.info("测温报警规则配置数据获取成功！预置点：" + cond.wPresetNo
                            + "，使能：" + alarmRule.struThermometryAlarmRuleParam[i].byEnabled
                            + ",规则ID：" + alarmRule.struThermometryAlarmRuleParam[i].byRuleID
                            + ",报警温度比较方式：" + alarmRule.struThermometryAlarmRuleParam[i].byRule
                            + ",报警规则名称：" + new String(alarmRule.struThermometryAlarmRuleParam[i].szRuleName, "GBK").trim()
                            + ",预警温度：" + alarmRule.struThermometryAlarmRuleParam[i].fAlert
                            + ",报警温度：" + alarmRule.struThermometryAlarmRuleParam[i].fAlarm
                            + ",门限温度：" + alarmRule.struThermometryAlarmRuleParam[i].fThreshold
                            + ",温度预警等待时间：" + alarmRule.struThermometryAlarmRuleParam[i].dwAlertFilteringTime
                            + ",温度报警等待时间：" + alarmRule.struThermometryAlarmRuleParam[i].dwAlarmFilteringTime
                    );
                } catch (UnsupportedEncodingException e) {
                    throw new MediaException("获取测温报警规则配置数据失败,错误原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                }
            }
        }
    }

    /**
     * 设置联动配置-上传中心
     *
     * @param uid           用户ID
     * @param channel       通道号
     * @param locationIndex 预置位号
     * @return boolean
     */
    private static boolean setLinkageConfig(long uid, int channel, int locationIndex) {
        // 先GET
        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_TRIGGER_COND cond = new HCNetSDK.NET_DVR_THERMOMETRY_TRIGGER_COND();
        HCNetSDK.NET_DVR_EVENT_TRIGGER trigger = new HCNetSDK.NET_DVR_EVENT_TRIGGER();

        cond.dwSize = cond.size();
        cond.dwChan = channel;
        // 预置点位号
        cond.dwPreset = (short) locationIndex;
        cond.write();

        stdConfig.lpCondBuffer = cond.getPointer();
        stdConfig.dwCondSize = cond.dwSize;

        stdConfig.lpOutBuffer = trigger.getPointer();
        stdConfig.dwOutSize = trigger.size();
        stdConfig.byDataType = 0;
        trigger.write();
        stdConfig.write();

        boolean bool = HC_NET_SDK.NET_DVR_GetSTDConfig((int) uid, 3632, stdConfig);
        if (!bool) {
            log.error("联动配置数据获取失败！！! 错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("联动配置数据获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            trigger.read();
            // 再SET
            stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
            stdConfig.lpCondBuffer = cond.getPointer();
            stdConfig.dwCondSize = cond.dwSize;
            HCNetSDK.BYTE_ARRAY byteArray = new HCNetSDK.BYTE_ARRAY(4096 * 4);
            stdConfig.lpStatusBuffer = byteArray.getPointer();
            stdConfig.dwStatusSize = 4096 * 4;
            trigger.dwSize = trigger.size();
            // 设置联动方式为上传中心
            trigger.struHandleException.dwHandleType = 4;
            trigger.write();

            stdConfig.lpInBuffer = trigger.getPointer();
            stdConfig.dwInSize = trigger.dwSize;
            stdConfig.byDataType = 0;
            stdConfig.write();

            bool = HC_NET_SDK.NET_DVR_SetSTDConfig((int) uid, 3633, stdConfig);
            if (!bool) {
                log.error("预置点:{},联动配置设置失败！！! 错误码：{},错误原因：{}", locationIndex, HC_NET_SDK.NET_DVR_GetLastError(),
                        HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                throw new MediaException("联动配置设置失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            } else {
                log.info("预置点：{},联动配置设置成功", locationIndex);
                return true;
            }
        }
    }

    /**
     * 获取联动配置
     *
     * @param uid           用户ID
     * @param channel       通道号
     * @param locationIndex 预置位号
     */
    private static void getLinkageConfig(int uid, int channel, int locationIndex) {
        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_TRIGGER_COND cond = new HCNetSDK.NET_DVR_THERMOMETRY_TRIGGER_COND();
        HCNetSDK.NET_DVR_EVENT_TRIGGER trigger = new HCNetSDK.NET_DVR_EVENT_TRIGGER();

        cond.dwSize = cond.size();
        cond.dwChan = channel;
        // 预置点位号
        cond.dwPreset = (short) locationIndex;
        cond.write();

        stdConfig.lpCondBuffer = cond.getPointer();
        stdConfig.dwCondSize = cond.dwSize;

        stdConfig.lpOutBuffer = trigger.getPointer();
        stdConfig.dwOutSize = trigger.size();
        stdConfig.byDataType = 0;
        trigger.write();
        stdConfig.write();

        boolean bool = HC_NET_SDK.NET_DVR_GetSTDConfig(uid, 3632, stdConfig);
        if (!bool) {
            log.error("联动配置数据获取失败！！! 错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("联动配置数据获取失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            trigger.read();
            log.info("联动配置数据获取成功");
            log.info("异常处理方式:{}", trigger.struHandleException.dwHandleType);
        }
    }

    /**
     * 设置测温基本参数
     *
     * @param uid     用户ID
     * @param channel 通道号
     */
    private static boolean setBasicParam(long uid, int channel) {
        // 先GET
        IntByReference pchannel = new IntByReference(channel);
        Pointer pChannelNum = pchannel.getPointer();

        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_BASICPARAM basicParam = new HCNetSDK.NET_DVR_THERMOMETRY_BASICPARAM();

        stdConfig.lpCondBuffer = pChannelNum;
        stdConfig.dwCondSize = 4;
        stdConfig.lpInBuffer = null;
        stdConfig.dwInSize = 0;
        stdConfig.lpOutBuffer = basicParam.getPointer();
        stdConfig.dwOutSize = basicParam.size();
        stdConfig.write();
        basicParam.write();

        if (!HC_NET_SDK.NET_DVR_GetSTDConfig((int) uid, 3621, stdConfig)) {
            log.error("获取测温基本参数失败，错误码：{},错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("获取测温基本参数失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            basicParam.read();
            // 再SET
            stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
            stdConfig.lpCondBuffer = pChannelNum;
            stdConfig.dwCondSize = 4;
            HCNetSDK.BYTE_ARRAY m_szStatusBuf = new HCNetSDK.BYTE_ARRAY(4096 * 4);
            stdConfig.lpStatusBuffer = m_szStatusBuf.getPointer();
            stdConfig.dwStatusSize = 4096 * 4;
            basicParam.dwSize = basicParam.size();

            // 使能：0- 不启用，1- 启用
            basicParam.byEnabled = 1;
            // 码流是否叠加温度信息：0- 否，1- 是
            basicParam.byStreamOverlay = 1;
            // 抓图是否叠加温度信息：0- 否，1- 是
            basicParam.byPictureOverlay = 1;
            // 测温范围（这里以摄氏度为单位计算，其他单位由上层自行转换）：0- 默认值，1- (-20~150)，2- (0~550)，3- (0~650），4- （-40~150），5-（0~1200），6- （-20-120），0xff- 自动
            basicParam.byThermometryRange = 1;
            // 测温单位（所有测温配置功能中温度参数对应的单位）: 0- 摄氏度（℃），1- 华氏度（℉），2- 开尔文(K)
            basicParam.byThermometryUnit = 0;
            // 测温曲线模式显示方式：0- 关闭，1- 模式1(横向温度趋势线模式)，2- 模式2(纵向温度趋势线模式)
            basicParam.byThermometryCurve = 1;
            // 消防图像模式：0- 保留，1- 黑白模式，2- 热探测模式，3- 火场模式
            basicParam.byFireImageModea = 2;
            // 显示温度条使能：0- 否，1- 是
            basicParam.byShowTempStripEnable = 1;
            // 发射率(即物体向外辐射能量的本领，精确到小数点后两位)，取值范围：[0.01, 1.00],该值对于不支持规则框以及预置点的设备使用
            basicParam.fEmissivity = (float) 0.98;
            // 距离单位: 0- 米(m)，1- 英尺(feet)，2-厘米（cm）
            basicParam.byDistanceUnit = 2;
            // 距离，单位：米(m)，取值范围：[0,10000]
            basicParam.wDistance = 1000;
            // 显示最高温：0- 不显示，1- 显示
            basicParam.byDisplayMaxTemperatureEnabled = 1;
            // 显示最低温：0- 不显示，1- 显示
            basicParam.byDisplayMinTemperatureEnabled = 1;
            // 显示平均温：0- 不显示，1- 显示
            basicParam.byDisplayAverageTemperatureEnabled = 1;

            basicParam.write();
            stdConfig.lpInBuffer = basicParam.getPointer();
            stdConfig.dwInSize = basicParam.dwSize;
            stdConfig.write();
            if (!HC_NET_SDK.NET_DVR_SetSTDConfig((int) uid, 3622, stdConfig)) {
                log.error("设置测温基本参数失败，错误码：{}错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                        HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
                throw new MediaException("设置测温基本参数失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            } else {
                log.info("设置测温基本参数成功");
                return true;
            }
        }
    }

    /**
     * 获取测温基本参数
     *
     * @param uid     用户ID
     * @param channel 通道号
     */
    private static void getBasicParam(int uid, int channel) {
        IntByReference pchannel = new IntByReference(channel);
        Pointer pChannelNum = pchannel.getPointer();

        HCNetSDK.NET_DVR_STD_CONFIG stdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_DVR_THERMOMETRY_BASICPARAM basicParam = new HCNetSDK.NET_DVR_THERMOMETRY_BASICPARAM();

        stdConfig.lpCondBuffer = pChannelNum;
        stdConfig.dwCondSize = 4;
        stdConfig.lpInBuffer = null;
        stdConfig.dwInSize = 0;
        stdConfig.lpOutBuffer = basicParam.getPointer();
        stdConfig.dwOutSize = basicParam.size();
        stdConfig.write();
        basicParam.write();

        if (!HC_NET_SDK.NET_DVR_GetSTDConfig(uid, 3621, stdConfig)) {
            log.error("获取测温基本参数失败，错误码：{}错误原因：{}", HC_NET_SDK.NET_DVR_GetLastError(),
                    HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
            throw new MediaException("获取测温基本参数失败，原因：" + HkErrorEnum.getErrorMsgByCode(HC_NET_SDK.NET_DVR_GetLastError()));
        } else {
            basicParam.read();
            log.info("获取测温基本参数成功，测温使能：" + basicParam.byEnabled + " 码流叠加温度信息：" + basicParam.byStreamOverlay
                    + " 抓图叠加温度信息：" + basicParam.byPictureOverlay + " 测温范围：" + basicParam.byThermometryRange
                    + " 测温单位：" + basicParam.byThermometryUnit + " 显示温度条：" + basicParam.byShowTempStripEnable
                    + " 发射率：" + basicParam.fEmissivity + " 距离单位：" + basicParam.byDistanceUnit + " 距离：" + basicParam.wDistance
                    + " 特殊测温点显示：" + basicParam.bySpecialPointThermType + " 最高温显示：" + basicParam.byDisplayMaxTemperatureEnabled
                    + " 最低温显示：" + basicParam.byDisplayMinTemperatureEnabled + " 平均温显示：" + basicParam.byDisplayAverageTemperatureEnabled);
        }
    }

}
